iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 19
0
Modern Web

實作小範例入門 Vue & Vuex 2.0系列 第 19

vue & vuex 19 - Shopping cart - I (v-bind, :class, 數據驅動)

  • 分享至 

  • xImage
  •  

接下來兩天會實作簡單 Shopping cart 主要練習,數據驅動 UI


我們是一間高級餐廳,每日會提供限量餐點,因為預訂的客戶太多希望可以開發一套餐點預訂系統。

今天目標:

  1. 餐點陳列頁面,顯示限量餐點與它的剩餘數量。
  2. 餐點下方有購物車按鈕,加入購物車後剩餘數量 -1,並放到購物車中。
  3. 右上方有購物車圖示,會顯示購物車內餐點總數量。
  4. 當餐點剩下最後一客的時候,按鈕希望變成紅色。
  5. 當餐點被預訂完的時候,希望餐點要反灰,顯示售完,不能預訂。

系統規劃

vue & vuex 19 Shopping Cart

  • 餐廳首頁: shop.vue (今天)
  • 購物車頁: cart.vue (明天)

資料結構:

{
  title: String,
  image: String,
  inventory: Number,
  price: Number
},

數據驅動 UI

主要使用 v-bind:class 語法糖: :class

取代過去我們使用 JQuery 在條件成立的時候要把 tag 的 class 換掉,

在 Vue 框架下可以使用數據,來驅動 UI 的變化,

使用聲明式宣告,把所有條件寫好在 UI 上,只要數據改變,UI 就會改變。

如果將 web 形容成人體的話:

web 人體
HTML 骨頭
CSS 皮膚
JavaScript 肌肉
JSON 神經

比喻:被蚊子叮的時候..

web 被蚊子叮的時候 人體 vue & vuex
JSON 神經反應 被叮了 vuex 改變 staet
JavaScript 肌肉收縮 X! 打蚊子! state 改變 UI 更新資料
CSS 皮膚改變 腫起來.. (用指甲畫十字) 改變 class 或 style

以上是練肖威.. :D


v-bind:class

使用 Object 作為結構,

Object 功能
key 條件成立時使用的 class
value 條件設定 (javascript)

v-bind:disabled

條件成立的時候,觸發 disabled

pages/shop.vue

<template>
<div>
  <!-- Top menu -->
  <div class="w3-white w3-xlarge w3-padding-xlarge" style="max-width:1200px;margin:auto">
    <div class="w3-right">
      <router-link :to="{name: 'cart'}">
        <!-- 顯示購物車總產品數 -->
        <span class="glyphicon glyphicon-shopping-cart" aria-hidden="true"> {{ cartTotal }}</span>
      </router-link>
    </div>
    <div class="w3-center">Shop</div>
  </div>
  <!-- !PAGE CONTENT! -->
  <div class="w3-main w3-content w3-padding" style="max-width:1200px;margin-top:100px">
    <div class="w3-row-padding w3-padding-16 w3-center" id="food">
      <!-- 
        產品列表
        當產品庫存為: 0 
        使用 :class 加上 "w3-grayscale-max" class
      -->
      <div 
        v-for="(item, index) in foodList"
        class="w3-quarter" 
        :class="{ 'w3-grayscale-max': !item.inventory }">
        <img :src="item.image" style="width: 100%;">
        <h3>{{ item.title }}</h3>
        <h4>${{ item.price }}</h4>
        <!-- 
          數據驅動 UI
          根據條件變化不同 class
          當庫存為:0 的時候 ":disabled"
        -->
        <button 
          class="w3-btn w3-round-large w3-large w3-padding-large"
          :class="{
            'w3-red':  item.inventory == 1,
            'w3-green': item.inventory >= 2,
            'w3-dark-grey': !item.inventory
          }"
          :disabled="!item.inventory"
          @click="addCart( item.title )">
          <span class="glyphicon glyphicon-shopping-cart" aria-hidden="true"></span>
          <!-- 
            數據驅動 UI
            按鈕顯示文案
          -->
          <span v-if="item.inventory == 1">最後 {{ item.inventory }} 客</span>
          <span v-if="item.inventory >= 2">限量 {{ item.inventory }} 客</span>
          <span v-if="!item.inventory">Sold out</span>
        </button>
      </div><!-- end preduct -->
    </div>
  </div>
</div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex';

export default {
  computed: mapGetters({
    foodList: 'getProducts',
    cartTotal: 'getShoppingCartTotal'
  }),
  methods: mapActions([
    'addCart'
  ]),
}
</script>
<!-- 載入 w3school - food blog template -->
<style src="../../static/css/w3.css"></style>

store/modules/shop.js

const types = {
  ADD_CART: 'store/ADD_CART',
  CANCEL_CART: 'store/CANCEL_CART'
}

const state = {
  // 餐點列表
  products: [
    {
      title: 'The Perfect Sandwich, A Real NYC Classic',
      image: 'http://www.w3schools.com/w3images/sandwich.jpg',
      inventory: 5,
      price: 155
    },
    {
      title: 'Let Me Tell You About This Steak',
      image: 'http://www.w3schools.com/w3images/steak.jpg',
      inventory: 1,
      price: 1380
    },
    {
      title: 'Cherries, interrupted',
      image: 'http://www.w3schools.com/w3images/cherries.jpg',
      inventory: 2,
      price: 499
    },
    {
      title: 'Once Again, Robust Wine and Vegetable Pasta',
      image: 'http://www.w3schools.com/w3images/wine.jpg',
      inventory: 3,
      price: 790
    }
  ],
  // 購物車
  shoppingCart: [],
}

const getters = {
  // 取得餐點列表
  getProducts: state => state.products,
  // 取得購物車總數量
  getShoppingCartTotal: state => state.shoppingCart.length,
}

const actions = {
  addCart ({ commit }, id) {
    commit(types.ADD_CART, id);
  }
}

const mutations = {
  [types.ADD_CART] (state, id) {
    // ES6 array find 找到條件成立的內容。
    const product = state.products.find(item => item.title === id && item.inventory !== 0);
    // 餐點庫存 -1
    product.inventory = product.inventory - 1;
    // 餐點加入購物車 title, price
    state.shoppingCart.push({
      title: product.title,
      price: product.price,
    });
  }
}

export default {
  state,
  getters,
  actions,
  mutations
}

github 完整範例:

實作小範例入門 Vue & Vuex 2.0 - github 完整範例

使用 git checkout 切換每天範例。


上一篇
vue & vuex 18 - 開發小技巧
下一篇
vue & vuex 20 - Shopping cart - II (購物車,推薦商品加購)
系列文
實作小範例入門 Vue & Vuex 2.030
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言